use std::{fmt::Debug, sync::Arc};

use axum::{async_trait, extract::FromRequestParts, http::header, http::request::Parts};
use serde::{de::DeserializeOwned, Serialize};

use crate::{
    config,
    jwt::{token, Claims, HasRole},
    AppState, Error, Result,
};

pub struct RequiredAuth<T>(pub Claims<T>);

#[async_trait]
impl<T> FromRequestParts<Arc<AppState>> for RequiredAuth<T>
where
    T: Serialize + DeserializeOwned + Debug + HasRole,
{
    type Rejection = Error;

    async fn from_request_parts(
        parts: &mut Parts,
        state: &Arc<AppState>,
    ) -> std::result::Result<Self, Self::Rejection> {
        tracing::debug!("[RequiredAuth middleware] begin");

        let location = parts
            .headers
            .get("x-path")
            .and_then(|value| value.to_str().ok());
        tracing::debug!("location: {location:?}");

        let path = location.unwrap_or(parts.uri.path());
        let is_admin = path.starts_with("/admin");
        tracing::debug!(
            "[RequiredAuth middleware] request path: {path}, is_admin_path: {is_admin}"
        );

        let auth_header = parts
            .headers
            .get(header::AUTHORIZATION)
            .and_then(|value| value.to_str().ok());

        tracing::debug!("[RequiredAuth middleware] auth_header: {:?}", auth_header);

        let auth_header = match auth_header {
            Some(auth_header) => match auth_header.split(" ").last().to_owned() {
                Some(v) => v,
                None => return Err(Error::forbidden("请登录")),
            },
            _ => return Err(Error::forbidden("请登录")),
        };
        tracing::debug!("[RequiredAuth middleware] auth_header: {:?}", auth_header);

        let cfg = if is_admin {
            &state.cfg.admin_jwt
        } else {
            &state.cfg.user_jwt
        };

        let ra = match token_is_valid::<T>(auth_header, cfg) {
            Ok(v) => v,
            Err(e) => return Err(e),
        };

        if is_admin {
            if !ra.data.role().is_admin() {
                return Err(Error::forbidden("需要管理员权限"));
            }
        }

        Ok(RequiredAuth(ra))
    }
}
fn token_is_valid<T: Serialize + DeserializeOwned + Debug + HasRole>(
    token_str: &str,
    cfg: &config::JwtConfig,
) -> Result<Claims<T>> {
    let r = token::decode(token_str, cfg);

    tracing::debug!("[RequireAuth middleware] token_is_valid: {:?}", r);

    r
}
